﻿using System;
using System.Collections.Generic;
using System.Linq;

// Window Events . 
using System.Windows;
// Reflection .
using System.Reflection;


// MEF .
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;



namespace DailyFx.Studio
{
	using DailyFx.UI;

	public class Bootstrapper : DailyFx.UI.Bootstrapper 
	{
		/// <summary>
		///		the Meffed Composition .
		/// </summary>
		private CompositionContainer _composition;


		protected override void BootIfDesigner()
		{
			// Dummy .
			base.BootIfDesigner();
		}

		
		protected override void BootIfApplication ()
		{
			base.BootIfApplication();

			// By default, we are configured to use MEF
			//

			// Gets the assemblies already loaded .
			Assembly[] assemblies = new Assembly[] 
			{ 
				Assembly.GetEntryAssembly(),	// DailyFx.Studio
				typeof(IWindow).Assembly		// DailyFx.UI
			};

			AggregateCatalog catalog = new AggregateCatalog (
				assemblies.Select ( x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>() );

			// Gets the addins of services .
			// catalog.Catalogs.Add(new DirectoryCatalog( Environment.CurrentDirectory + "\\Addins") );

			var batch = new CompositionBatch();

				batch.AddExportedValue<IWindowManager>(new WindowManager());
				batch.AddExportedValue<IMediator>(new Mediator());
			
			this._composition = new CompositionContainer( catalog);
			this._composition.Compose( batch );

//			// Extend the name rule of ViewLocator, for main window : MainViewModel -> MainWindow
//			Caliburn.Micro.ViewLocator.NameTransformer.AddRule("ViewModel$", "Window");
//			// Extend the name rule of ViewLocator, for UI Part Manager : *Manager -> *View
//			Caliburn.Micro.ViewLocator.NameTransformer.AddRule("Manager$", "View");
//			// Extend the name rule of ViewLocator, for *Manip -> *View
//			Caliburn.Micro.ViewLocator.NameTransformer.AddRule("Manip", "View");
//			// Extend the name rule of ViewLocator, for *Impl -> *View
//			Caliburn.Micro.ViewLocator.NameTransformer.AddRule("Impl", "View");
		}


		/// <summary>
		/// 
		/// </summary>
		public override object GetService ( Type serviceType, string key )
		{
			if ( key == null )
			{
				key = AttributedModelServices.GetContractName( serviceType );
			}

			IEnumerable<Lazy<object>> exports = this._composition.GetExports<object>(key);
			if ( exports.Any() )
			{
				return exports.First().Value;
			}

			// missing ??
			throw new ArgumentException ( string.Format("Cannot locate instances of contract: {0}", key) );
		}

		/// <summary>
		/// 
		/// </summary>
		public override IEnumerable<object> GetServices ( Type serviceType )
		{
			var exports = this._composition.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
			return exports;
		}

		/// <summary>
		/// 
		/// </summary>
		protected override void SatisfyInstance (object instance)
		{
			// ??
			this._composition.SatisfyImportsOnce( instance );
		}
		

		protected override void OnStartup ( object sender, StartupEventArgs e)
		{

			var getNamedChildren = TreeHelper.GetNamedChildren;
			TreeHelper.GetNamedChildren = element =>
			{
				var metroWindow = element as MahApps.Metro.Controls.MetroWindow;
				if (metroWindow == null)
				{
					return getNamedChildren( element );
				}

				var list = new List<FrameworkElement>(getNamedChildren(element));
				var type = element.GetType();

				var fields =
					element.GetType()
					 .GetFields(BindingFlags.Instance | BindingFlags.NonPublic)
					 .Where(f => f.DeclaringType == type);

				// Visual Tree's not awared of flyout control ...
				var flyouts =
					fields.Where(f => f.FieldType == typeof(MahApps.Metro.Controls.FlyoutsControl))
						  .Select(f => f.GetValue(element))
						  .Cast<MahApps.Metro.Controls.FlyoutsControl>();

				list.AddRange(flyouts);
				return list;
			};

			// todo: resolves all views & viewModels ...
			//
			base.OnStartup(sender, e);

		}

		protected override void OnExit ( object sender, EventArgs e)
		{
			// todo:
			base.OnExit(sender, e);
		}


	}
}
